/**
 * 音频管理单例类
 * 职责：统一管理应用内所有音频资源，提供跨平台的音频控制能力
 * 特性：
 * - 音频实例池化管理
 * - 自动内存回收机制
 * - 预加载/懒加载双模式
 * - 全局音量控制
 * - 详细的错误监控
 * - 跨平台兼容（H5/小程序/APP）
 * - 本地和远程音频路径切换功能
 */
class AudioManager {
  constructor() {
    /**
     * 音频实例池
     * @type {Object.<string, InnerAudioContext>}
     * @description 
     * 结构示例：{
     *   'bgm': InnerAudioContext实例,
     *   'effect_click': InnerAudioContext实例
     * }
     */
    this.audioPool = {};

    /**
     * 音频资源库
     * @type {Object.<string, {online: string, local: string}>}
     * @description 预定义的音频资源URL映射表，包含在线和本地路径
     */
    this.audioSources = {
      jingdi: {
        online: 'https://tcwjdl.oss-cn-chengdu.aliyuncs.com/jingdi.wav',
        local: '/static/audio/jingdi.wav'
      },
      load_bgm: {
        online: 'https://tcwjdl.oss-cn-chengdu.aliyuncs.com/load_bgm.wav',
        local: '/static/audio/load_bgm.wav'
      }
    };

    /**
     * 平台标识
     * @type {boolean}
     * @description 区分H5环境与原生环境
     */
    this.isH5 = typeof window !== 'undefined';

    /**
     * 是否使用网络路径（全局开关）
     * @type {boolean}
     */
    this.useOnlinePath = false;

    /**
     * 路径前缀配置
     * @type {{online: string, local: string}}
     */
    this.basePaths = {
      online: 'https://your-cdn.com/project/audio/',
      local: '/static/audio/'
    };
  }

  // ================== 核心方法 ================== //

  /**
   * 获取当前路径前缀
   * @returns {string}
   */
  get currentBase() {
    return this.useOnlinePath ? this.basePaths.online : this.basePaths.local;
  }

  /**
   * 获取音频资源地址
   * @param {string} audioId - 音频资源标识符
   * @returns {string}
   * @throws {Error} 当音频ID不存在时抛出错误
   * @example
   * const url = audioManager.getSource('bgm');
   */
  getSource(audioId) {
    const source = this.audioSources[audioId];
    if (!source) {
      throw new Error(`[AudioManager] 音频资源未注册: ${audioId}`);
    }
    return this.currentBase + (this.useOnlinePath ? source.online : source.local);
  }

  /**
   * 创建或获取音频实例
   * @param {string} audioId - 音频资源标识符
   * @returns {InnerAudioContext}
   * @description 
   * 1. 检查实例池是否已存在
   * 2. 新建实例并配置基础参数
   * 3. 绑定生命周期事件
   */
  getInstance(audioId) {
    if (!this.audioPool[audioId]) {
      // 创建音频上下文
      const audio = uni.createInnerAudioContext();

      // 配置基础参数
      audio.src = this.getSource(audioId);
      audio.volume = 0.8; // 默认音量

      // 事件绑定（使用箭头函数保持this指向）
      const handleError = (err) => {
        console.error(`[AudioManager] 播放错误 ${audioId}:`, err);
        this.destroy(audioId);
      };

      audio.onPlay(() => this.log(audioId, '开始播放'));
      audio.onPause(() => this.log(audioId, '暂停播放'));
      audio.onStop(() => this.log(audioId, '停止播放'));
      audio.onEnded(() => {
        this.log(audioId, '播放结束');
        if (!audio.loop) this.destroy(audioId); // 非循环播放自动销毁
      });
      audio.onError(handleError);

      // 加入实例池
      this.audioPool[audioId] = audio;
    }
    return this.audioPool[audioId];
  }

  // ================== 播放控制 ================== //

  /**
   * 播放音频
   * @param {string} audioId - 音频资源标识符
   * @param {Object} [options] - 播放选项
   * @param {boolean} [options.loop=false] - 是否循环播放
   * @param {number} [options.volume=0.8] - 初始音量(0-1)
   * @param {boolean} [options.lazyLoad=true] - 是否懒加载
   */
  async play(audioId, { 
    loop = false, 
    volume = 0.8, 
    lazyLoad = true 
  } = {}) {
    try {
      const audio = lazyLoad ? this.getInstance(audioId) : await this.preload(audioId);
      
      // 参数配置
      audio.loop = loop;
      audio.volume = Math.min(1, Math.max(0, volume));

      // H5环境自动播放处理
      if (this.isH5) {
        const playPromise = audio.play();
        if (playPromise !== undefined) {
          playPromise.catch(err => {
            console.warn(`[AudioManager] 自动播放被阻止: ${err}`);
            // 可以在这里触发用户交互后重试
          });
        }
      } else {
        audio.play();
      }
    } catch (error) {
      console.error(`[AudioManager] 播放失败: ${error.message}`);
    }
  }

  /**
   * 预加载音频资源
   * @param {string} audioId - 音频资源标识符
   * @returns {Promise<InnerAudioContext>}
   * @description 提前加载音频文件以减少播放延迟
   */
  preload(audioId) {
    return new Promise((resolve, reject) => {
      try {
        const audio = this.getInstance(audioId);
        
        // H5需要等待canplay事件
        if (this.isH5) {
          const canplayHandler = () => {
            audio.offCanplay(canplayHandler);
            resolve(audio);
          };
          audio.onCanplay(canplayHandler);
        } else {
          // 小程序/APP无需等待
          resolve(audio);
        }
      } catch (error) {
        reject(error);
      }
    });
  }

  // ================== 生命周期管理 ================== //

  /**
   * 销毁音频实例
   * @param {string} audioId - 音频资源标识符
   * @description 
   * 1. 停止播放
   * 2. 释放资源
   * 3. 移除实例引用
   */
  destroy(audioId) {
    const audio = this.audioPool[audioId];
    if (audio) {
      audio.stop();
      audio.destroy();
      delete this.audioPool[audioId];
      this.log(audioId, '实例已销毁');
    }
  }

  /**
   * 销毁所有音频实例
   * @description 通常在应用退出时调用
   */
  destroyAll() {
    Object.keys(this.audioPool).forEach(audioId => {
      this.destroy(audioId);
    });
  }

  // ================== 工具方法 ================== //

  /**
   * 统一日志记录
   * @param {string} audioId - 音频资源标识符
   * @param {string} message - 日志信息
   */
  log(audioId, message) {
    console.debug(`[AudioManager] ${audioId.padEnd(12)} => ${message}`);
  }

  /**
   * 全局音量控制
   * @param {number} volume - 目标音量(0-1)
   */
  setGlobalVolume(volume) {
    const validVolume = Math.min(1, Math.max(0, volume));
    Object.values(this.audioPool).forEach(audio => {
      audio.volume = validVolume;
    });
  }

  /**
   * 切换路径类型（true=网络路径，false=本地路径）
   * @param {boolean} isOnline
   */
  switchPathType(isOnline) {
    this.useOnlinePath = !!isOnline;
    console.log(`已切换到${this.useOnlinePath ? '网络' : '本地'}路径`);
  }
}

// 单例导出（确保全局唯一实例）
export const audioManager = new AudioManager();
    